package sc.digital.human;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import java.io.InputStream;
import java.io.OutputStream;

public class Audio {
    private static final Logger logger = LoggerFactory.getLogger(Audio.class);
    private final byte[] buffer;
    private final AudioFormat audio;
    private SourceDataLine source;
    private TargetDataLine target;
    private volatile boolean isTracking, isRecording;
    private Thread tracking, recording;

    public Audio(byte[] buffer) {
        this.buffer = buffer;
        this.audio = new AudioFormat(Config.SAMPLE_RATE, Config.SAMPLE_SIZE_BITS, Config.CHANNELS_NUMBER
                , true, false);
    }

    public float getDurationInSeconds(byte[] buffer) {
        return buffer.length / (this.audio.getSampleRate() * this.audio.getSampleSizeInBits() * this.audio.getChannels() / 8);
    }

    public synchronized void openTracking() {
        if (this.source == null) {
            try {
                this.source = AudioSystem.getSourceDataLine(this.audio);
                this.source.open(this.audio);
                this.source.start();
                if (logger.isInfoEnabled()) {
                    logger.info("播放打开：>>>>>>>>>>");
                }
            } catch (Exception e) {
                this.closeTracking();
                if (logger.isErrorEnabled()) {
                    logger.error("播放打开：{}", e.getMessage(), e);
                }
                throw new RuntimeException(e);
            }
        }
    }

    public synchronized void startTracking(Callback callback, InputStream is) {
        this.openTracking();
        if (this.tracking == null) {
            this.isTracking = true;
            this.tracking = new Thread(() -> {
                try {
                    int i;
                    while (this.isTracking && (i = is.read(buffer, 0, buffer.length)) > 0) {
                        this.source.write(this.buffer, 0, i);
                        if (callback != null) {
                            callback.process(buffer, 0, i);
                        }
                    }
                } catch (Exception e) {
                    this.closeRecording();
                    if (logger.isErrorEnabled()) {
                        logger.error("播放开始：{}", e.getMessage(), e);
                    }
                    throw new RuntimeException(e);
                } finally {
                    this.stopRecording();
                }
            });
            this.tracking.start();
            if (logger.isInfoEnabled()) {
                logger.info("播放开始：>>>>>>>>>>");
            }
        }
    }

    public synchronized void stopTracking() {
        this.isTracking = false;
        if (this.tracking != null) {
            this.tracking.interrupt();
            this.tracking = null;
            if (logger.isInfoEnabled()) {
                logger.info("播放停止：<<<<<<<<<<");
            }
        }
    }

    public synchronized void closeTracking() {
        this.stopTracking();
        if (this.source != null) {
            this.source.stop();
            this.source.close();
            this.source = null;
            if (logger.isInfoEnabled()) {
                logger.info("播放关闭：<<<<<<<<<<");
            }
        }
    }

    public synchronized void openRecording() {
        if (this.target == null) {
            try {
                this.target = AudioSystem.getTargetDataLine(this.audio);
                this.target.open(this.audio);
                this.target.start();
                if (logger.isInfoEnabled()) {
                    logger.info("录音打开：>>>>>>>>>>");
                }
            } catch (Exception e) {
                this.closeRecording();
                if (logger.isErrorEnabled()) {
                    logger.error("录音打开：{}", e.getMessage(), e);
                }
                throw new RuntimeException(e);
            }
        }
    }

    public synchronized void startRecording(Callback callback, OutputStream os) {
        this.openRecording();
        if (this.recording == null) {
            this.isRecording = true;
            this.recording = new Thread(() -> {
                try {
                    int i;
                    while (this.isRecording && (i = target.read(buffer, 0, buffer.length)) > 0) {
                        if (os != null) {
                            os.write(this.buffer, 0, i);
                        }
                        if (callback != null) {
                            callback.process(buffer, 0, i);
                        }
                    }
                } catch (Exception e) {
                    this.closeRecording();
                    if (logger.isErrorEnabled()) {
                        logger.error("录音开始：{}", e.getMessage(), e);
                    }
                    throw new RuntimeException(e);
                } finally {
                    this.stopRecording();
                }
            });
            this.recording.start();
            if (logger.isInfoEnabled()) {
                logger.info("录音开始：>>>>>>>>>>");
            }
        }
    }

    public synchronized void stopRecording() {
        this.isRecording = false;
        if (this.recording != null) {
            this.recording.interrupt();
            this.recording = null;
            if (logger.isInfoEnabled()) {
                logger.info("录音停止：<<<<<<<<<<");
            }
        }
    }

    public synchronized void closeRecording() {
        this.stopRecording();
        if (this.target != null) {
            this.target.stop();
            this.target.close();
            this.target = null;
            if (logger.isInfoEnabled()) {
                logger.info("录音关闭：<<<<<<<<<<");
            }
        }
    }

    interface Callback {
        void process(byte[] buffer, int offset, int length);
    }
}
